﻿#if UNITY_EDITOR

namespace Blaze.FileSystems
{
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using Utilities;

    public class PkgBuilder
    {
        public class DumpInfo
        {
            public int BlockIndex;
            public ulong Hash;
            public string Path;
        }

        public PkgBuilder(Stream output)
        {
            mStream = output;
            output.Seek(mHeaderSize, SeekOrigin.Begin);
            mWriter = new BinaryWriter(output);
        }

        public static List<DumpInfo> LoadDump(Stream input)
        {
            var reader = new StreamReader(input);
            var items = new List<DumpInfo>();
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                var columns = line.Split(',');
                items.Add(new DumpInfo
                {
                    Hash = ulong.Parse(columns[0]),
                    BlockIndex = int.Parse(columns[1]),
                    Path = columns[2]
                });
            }
            return items;
        }

        public void Add(string path, Stream stream)
        {
            using (var reader = new BinaryReader(stream))
            {
                var bytes = reader.ReadBytes((int) stream.Length);
                var hash = bytes.Hash();
                int blockIndex;
                if (!mHashToBlockIndex.TryGetValue(hash, out blockIndex))
                {
                    var blockTableItem = new PkgFile.BlockTableItem
                    {
                        Size = bytes.Length,
                        Position = (int) mStream.Position,
                    };
                    mBlockTableItems.Add(blockTableItem);
                    blockIndex = mBlockTableItems.Count - 1;
                    mHashToBlockIndex.Add(hash, blockIndex);
                    mWriter.Write(bytes);
                }
                var hashTableItem = new PkgFile.HashTableItem
                {
                    Hash = XXHash64.Hash(path),
                    BlockIndex = blockIndex,
                };
                mHashTableItems.Add(hashTableItem);
                mDumpInfos.Add(new DumpInfo
                {
                    BlockIndex = blockIndex,
                    Hash = hash,
                    Path = path
                });
            }
        }

        public void Dump(Stream output)
        {
            var writer = new StreamWriter(output);
            foreach (var info in mDumpInfos)
                writer.WriteLine("{0},{1},{2}", info.Hash, info.BlockIndex, info.Path);
            writer.Flush();
        }

        public void Finish()
        {
            var hashTablePosition = (int) mStream.Position;
            foreach (var item in mHashTableItems)
                item.Write(mWriter);
            var blockTablePosition = (int) mStream.Position;
            foreach (var item in mBlockTableItems)
                item.Write(mWriter);

            var header = new PkgFile.FileHeader
            {
                Magic = "PKG",
                Version = 1,
                HashTablePosition = hashTablePosition,
                HashTableItemCount = mHashTableItems.Count,
                BlockTablePosition = blockTablePosition,
                BlockTableItemCount = mBlockTableItems.Count,
            };

            mStream.Seek(0, SeekOrigin.Begin);
            header.Write(mWriter);
            mWriter.Flush();
        }

        private readonly BinaryWriter mWriter;
        private readonly Stream mStream;
        private readonly Dictionary<ulong, int> mHashToBlockIndex = new Dictionary<ulong, int>();
        private readonly List<PkgFile.HashTableItem> mHashTableItems = new List<PkgFile.HashTableItem>();
        private readonly List<PkgFile.BlockTableItem> mBlockTableItems = new List<PkgFile.BlockTableItem>();
        private static readonly int mHeaderSize = Marshal.SizeOf<PkgFile.FileHeader>();
        private readonly List<DumpInfo> mDumpInfos = new List<DumpInfo>();
    }
}

#endif